package com.mjk.common.core.jdbc.datasource;

import com.google.common.base.Preconditions;

import com.mjk.common.base.error.BizException;
import com.mjk.common.core.jdbc.annotation.ArcSharding;
import com.mjk.common.core.jdbc.service.plus.provider.BeeDataSourceType;
import com.mjk.common.core.jdbc.service.plus.provider.impl.wrap.mongodb.MongoDataSource;
import com.mjk.common.core.jdbc.util.DataSourceUtil;
import com.mjk.common.util.ObjectUtils;
import com.mjk.common.util.PropertyUtil;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.zaxxer.hikari.HikariConfig;
import io.seata.rm.datasource.DataSourceProxy;
import lombok.Getter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.util.*;
import java.util.Map.Entry;


@Configuration
@EnableConfigurationProperties({ DataSourceProperties.class })
@ConditionalOnProperty(name ="sharding.jdbc.type", havingValue ="common")
public class CommonDataSourceAutoConf implements IDataSource {
	
	@Getter
	final Map<String, DataSource> dataSourceMap = new LinkedHashMap<>();
	
	@Getter
	Map<String, String> driverMap = new LinkedHashMap<>();
	
	final Map<String, JdbcTemplate> jdbcTemplateMap = new LinkedHashMap<>();
	
	@Getter
	private   String defaultName = "";

	public void setDefaultName(String defaultName) {
		this.defaultName = defaultName;
	}

	public void setDriverMap(Map<String, String> driverMap) {
		this.driverMap = driverMap;
	}
	 @Bean(name="minidaoDataSource")
	 public DataSource dataSource1(){
         return getJimuDataSource(dataSourceMap);
	 }
	@Bean
	@Primary
	public DynamicDataSource dataSource() {
		Map<Object, Object> targetDataSources = new HashMap<>();
		String defaultDataSource = "";
		for (Entry<String, DataSource> entry : dataSourceMap.entrySet()) {
			targetDataSources.put(entry.getKey(), entry.getValue());
			if(ObjectUtils.isEmpty(defaultDataSource))//第一个为默认连接池
				defaultDataSource = entry.getKey();
		}

		DynamicDataSource dataSource = new DynamicDataSource();
		dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
		dataSource.setDefaultTargetDataSource(targetDataSources.get(defaultDataSource));
		
		return dataSource;
	}

	@Override
	public void setEnvironment(Environment environment) {
		setDataSourceMap(environment);
	}

	@SuppressWarnings("unchecked")
	private void setDataSourceMap(final Environment environment) {
		String prefix = "sharding.jdbc.datasource.";
		String distrubTranscation = environment.getProperty("seata.enabled");
		String dataSources = environment.getProperty(prefix + "names");
		if (dataSources == null) {
			return;
		}
		for (String each : dataSources.split(",")) {
			try {
				Map<String, Object> dataSourceProps = PropertyUtil.handle(environment, prefix + each, Map.class);
				Preconditions.checkState(!dataSourceProps.isEmpty(), "Wrong datasource properties!");
				//判断是否是mongodb
				String type = dataSourceProps.get("type").toString();
				Preconditions.checkState(!ObjectUtils.isEmpty(type), "Wrong properties type!");
				if(BeeDataSourceType.MONGODB_WRAP.getDriver().equals(type)) {
					ConnectionString connString = new ConnectionString(dataSourceProps.get("jdbcUrl").toString());
					MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(connString)
							.retryWrites(true).build();
					MongoClient client = MongoClients.create(settings);
					dataSourceMap.put(each, new MongoDataSource(client));
					driverMap.put(each, type);
				}else {
					DataSource dataSource = DataSourceUtil.getDataSource(dataSourceProps.get("type").toString(),
							dataSourceProps);
					if("true".equals(distrubTranscation)) {
						dataSource = new DataSourceProxy(dataSource);
					}
					//initJdbcMapping(dataSourceProps.get("driver-class-name").toString(),dataSource);
					driverMap.put(each, dataSourceProps.get("driver-class-name").toString());	
					dataSourceMap.put(each, dataSource);
					JdbcTemplate jdbcTemplate = new JdbcTemplate();
					jdbcTemplate.setDataSource(dataSource);
					jdbcTemplateMap.put(each, jdbcTemplate);
				}
			} catch (final ReflectiveOperationException ex) {
				throw new BizException("Can't find datasource type!", ex);
			}
		}
		this.defaultName = environment.getProperty("sharding.jdbc.datasource.defaultname");
	}

	@Override
	public JdbcTemplate getJdbcTemplate(ArcSharding arcSharding) {
        if (null == arcSharding) {
            return this.jdbcTemplateMap.get(this.defaultName);
        }
		String dateSource = ObjectUtils.isNotEmpty(arcSharding.dataSource())?arcSharding.dataSource():this.defaultName;
		return this.jdbcTemplateMap.get(dateSource);
	}
	@Override
	public DataSource getDataSource(ArcSharding arcSharding) {
		if(null == arcSharding) {
			return  this.dataSourceMap.get(this.defaultName);
		}
		String dateSource = ObjectUtils.isNotEmpty(arcSharding.dataSource())?arcSharding.dataSource():this.defaultName;
		return this.dataSourceMap.get(dateSource);
	}

	@Override
	public Map<String, DataSource> getAllDataSource() {
		return this.dataSourceMap;
	}

	@Override
	public List<JdbcTemplate> getAllJdbcTemplate() {
		List<JdbcTemplate> result = new ArrayList<>();
		for(Entry<String, JdbcTemplate> entry : jdbcTemplateMap.entrySet()) {
			result.add(entry.getValue());
		}
		return result;
	}
}
